package com.kgis.map.control {

    import com.kgis.map.constant.IconConstant;
    import flash.display.DisplayObject;
    import flash.display.Graphics;
    import flash.events.Event;
    import flash.events.KeyboardEvent;
    import flash.events.MouseEvent;
    import flash.system.System;
    import flash.ui.Keyboard;

    import mx.containers.HBox;
    import mx.containers.TitleWindow;
    import mx.controls.Button;
    import mx.core.DragSource;
    import mx.core.FlexGlobals;
    import mx.core.IUIComponent;
    import mx.core.UIComponent;
    import mx.core.mx_internal;
    import mx.events.CloseEvent;
    import mx.events.DragEvent;
    import mx.events.MoveEvent;
    import mx.managers.CursorManager;
    import mx.managers.DragManager;

    use namespace mx_internal;

    public class EscapeWindow extends TitleWindow {

        // resize vars
        private const RESIZE_HANDLE_SIZE:int = 14;
        private var _resizeable:Boolean;
        private var _resizing:Boolean = false;
        private var resizeInitX:Number = 0;
        private var resizeInitY:Number = 0;
        protected var minResizeWidth:Number = 24;
        protected var minResizeHeight:Number = 24;

        // move vars
        private const MOVE_HANDLE_SIZE:int = 10;
        private var _movable:Boolean;
        private var _dragable:Boolean;
        private var _closeable:Boolean;
        private var moveInitX:Number = 0;
        private var moveInitY:Number = 0;

        private var _resizeHandle:UIComponent;
        private var _moveHandle:UIComponent;
        private var _dotColor:uint = 0x666666;
        private var myHbox:HBox;
        private var helpIcon:Button;
        private var _helpUrl:String = null;

        //Constant used to determine either ESC key pressed down or Close button clicked event is invoked
        public static const ESC:int = 2;
        public static const CLOSE:int = -1;

        public function EscapeWindow() {
            super();
            _resizeable = false;
            _movable = false;
            _dragable = true;
            _closeable = true;

            addEventListener(CloseEvent.CLOSE, closeHandler);
            addEventListener(KeyboardEvent.KEY_DOWN, keyHandler);

            addEventListener(KeyboardEvent.KEY_UP, onKeyUpHandler);
            addEventListener(MoveEvent.MOVE, onMoveHandler);
            addEventListener(MouseEvent.CLICK, onMouseClick);

            if (FlexGlobals.topLevelApplication != null) {
                FlexGlobals.topLevelApplication.addEventListener(Event.RESIZE, onAppResize, false, 0, true);
            }

            syncResizableState();
        }

        private var isRelocate:Boolean = false;

        private function onAppResize(event:Event):void {
            isRelocate = true;
            invalidateSize();
            invalidateDisplayList();
        }

        private function onMoveHandler(event:MoveEvent):void {
            if (this.x + this.width <= 25)
                this.x = 25 - width;
            if (this.x >= FlexGlobals.topLevelApplication.width - 25)
                this.x = FlexGlobals.topLevelApplication.width - 25;
            if (this.y <= 25 - this.titleBar.height)
                this.y = 25 - this.titleBar.height;
            if (this.y >= FlexGlobals.topLevelApplication.height - 25)
                this.y = FlexGlobals.topLevelApplication.height - 25;
        }

        [Inspectable(category="General")]
        /**
         *  Whether to display a Close button in the EscapeWindow container.
         *  Set it to <code>false</code> if you don't want to display the Close button.
         *  @default true
         *
         */
        private var closeableChanged:Boolean = false;

        [Bindable]
        public function get closeable():Boolean {
            return _closeable;
        }

        public function set closeable(value:Boolean):void {
            if (_closeable != value) {
                _closeable = value;
                closeableChanged = true;
                invalidateDisplayList();
            }
        }

        private var _closeButtonEnabled:Boolean = true;

        public function get closeButtonEnabled():Boolean {
            return _closeButtonEnabled;
        }

        public function set closeButtonEnabled(value:Boolean):void {
            _closeButtonEnabled = value;
            invalidateProperties();
        }

        [Inspectable(category="General", defaultValue="0x666666", format="Color", name="Resize/Move handle color")]
        public function get dotColor():uint {
            return _dotColor;
        }

        public function set dotColor(color:uint):void {
            _dotColor = color;
            if (resizeable || movable) {
                invalidateDisplayList();
            }
        }

        private function get moveHandle():UIComponent {
            if (_moveHandle == null) {
                _moveHandle = new UIComponent();
                _moveHandle.width = 8;
                _moveHandle.height = 14;
            }
            return _moveHandle;
        }

        private function get resizeHandle():UIComponent {
            if (_resizeHandle == null) {
                _resizeHandle = new UIComponent();
                _resizeHandle.width = RESIZE_HANDLE_SIZE;
                _resizeHandle.height = RESIZE_HANDLE_SIZE;
            }
            return _resizeHandle;
        }

        private var resizableChanged:Boolean;

        [Inspectable(category="General")]
        /**
         *  Whether to enable resizable window feature.
         *  The default value is <code>false</code>.
         *  Set it to <code>true</code> to resize the window.
         *
         *  @default false
         */
        public function get resizeable():Boolean {
            return _resizeable;
        }

        public function set resizeable(resize:Boolean):void {
            if (resize != _resizeable) {
                _resizeable = resize;
                resizableChanged = true;
                invalidateDisplayList();
            }
        }

        [Inspectable(category="General")]
        /**
         *  Whether to enable moving window feature.
         *  The default value is <code>false</code>.
         *  Set it to <code>true</code> to move the window.
         *
         *  @default false
         */
        public function get dragable():Boolean {
            return _dragable;
        }

        public function set dragable(drag:Boolean):void {
            if (drag != dragable) {
                _dragable = drag;
                this.isPopUp = drag;
            }
        }

        private var originalWidth:Number = -1;
        private var originalHeight:Number = -1;

        private function syncResizableState():void {
            if (_resizeable) {
                addEventListener(MouseEvent.MOUSE_DOWN, resizeHandler);
                addEventListener(MouseEvent.MOUSE_MOVE, resizeHandler);
                addEventListener(MouseEvent.MOUSE_OUT, resizeHandler);

                // set a minimum size for this container
                if (minWidth < minResizeWidth) {
                    minWidth = minResizeWidth;
                }
                if (minHeight < minResizeHeight) {
                    minHeight = minResizeHeight;
                }
            }
            else {
                if (resizeHandle.parent != null) {
                    removeChild(resizeHandle);
                }
                removeEventListener(MouseEvent.MOUSE_DOWN, resizeHandler);
                removeEventListener(MouseEvent.MOUSE_MOVE, resizeHandler);
                removeEventListener(MouseEvent.MOUSE_OUT, resizeHandler);
            }
        }

        [Inspectable(category="General")]
        public function get movable():Boolean {
            return _movable;
        }

        public function set movable(canMove:Boolean):void {
            if (canMove != _movable) {
                _movable = canMove;
                if (titleBar != null) {
                    if (movable) {
                        addMoveSupport();
                    }
                    else {
                        removeMoveSupport();
                    }
                }
            }
        }

        override protected function createChildren():void {
            super.createChildren();

            if (movable) {
                addMoveSupport();
            }
            if (closeable)
                showCloseButton = true;
            this.focusEnabled = true;
            this.focusManager;
            this.setFocus();
            this.isPopUp = _dragable;

            if (titleBar) {

                // Create an HBox in which to layout the button icons
                myHbox = new HBox();

                // Add the HBox and the icons to the titleBar display
                titleBar.addChild(myHbox);
            }
            if (this.closeButton) {
                this.closeButton.enabled = closeButtonEnabled && enabled;
            }
        }

        override protected function commitProperties():void {
            super.commitProperties();
            if (this.closeButton) {
                this.closeButton.enabled = closeButtonEnabled && enabled;
            }
        }

        private function addMoveSupport():void {
            if (titleBar) {
                titleBar.addChildAt(moveHandle, 0);
                titleBar.addEventListener(MouseEvent.MOUSE_DOWN, titleBarMouseDown);
            }
        }

        private function removeMoveSupport():void {
            if (titleBar) {
                titleBar.removeEventListener(MouseEvent.MOUSE_DOWN, titleBarMouseDown);
                if (moveHandle.parent != null) {
                    titleBar.removeChild(moveHandle);
                }
            }
        }

        protected function keyHandler(event:KeyboardEvent):void {
            if (event.keyCode == Keyboard.ESCAPE) {
                escapePressed();
            }
        }

        private function onKeyUpHandler(event:KeyboardEvent):void {
            if ((event.target.hasOwnProperty('displayAsPassword')) && (event.target.displayAsPassword == true)) {
                System.setClipboard(" ");
            }
        }

        protected function escapePressed():void {
            close(ESC);
        }

        public function close(detail:uint = 0):void {
            dispatchEvent(new CloseEvent(CloseEvent.CLOSE, false, false, detail));
        }

        override protected function measure():void {
            super.measure();
            measureAgainstApp();
        }

        /**
         * Measure the size by comparing to top application's size
         */
        protected function measureAgainstApp():void {
            var globalWidth:Number = FlexGlobals.topLevelApplication.width;
            var globalHeight:Number = FlexGlobals.topLevelApplication.height;

            originalWidth = measuredWidth > originalWidth ? measuredWidth : originalWidth;
            originalHeight = measuredHeight > originalHeight ? measuredHeight : originalHeight;

            if (measuredWidth > globalWidth) {
                measuredWidth = globalWidth * 0.8;
            }
            else {
                measuredWidth = originalWidth < globalWidth ? originalWidth : globalWidth * 0.8;
            }

            if (measuredHeight > globalHeight) {
                measuredHeight = globalHeight * 0.8;
            }
            else {
                measuredHeight = originalHeight < globalHeight ? originalHeight : globalHeight * 0.8;
            }
        }

        override protected function updateDisplayList(unscaledWidth:Number, unscaledHeight:Number):void {
            super.updateDisplayList(unscaledWidth, unscaledHeight);

            //update the width and height
            if (isRelocate) {
                isRelocate = false;
                x = (FlexGlobals.topLevelApplication.width - width) / 2;
                y = (FlexGlobals.topLevelApplication.height - height) / 2;
            }

            // Draw resize handle
            if (resizeable) {
                drawResizeHandle(unscaledWidth, unscaledHeight);
            }
            if (movable) {
                drawMoveHandle(unscaledWidth, unscaledHeight);
            }

            if ((helpUrl != null) && (titleBar != null)) {
                // Do this or the HBox won't appear!
                myHbox.setActualSize(myHbox.getExplicitOrMeasuredWidth(), myHbox.getExplicitOrMeasuredHeight());

                // Position the HBox
                var y:int = (titleBar.height - myHbox.getExplicitOrMeasuredHeight()) / 2;
                var x:int = this.width - myHbox.width - 12;
                myHbox.move(x, y);
            }

            if (closeableChanged) {
                closeableChanged = false;
            }

            if (resizableChanged) {
                resizableChanged = false;
                syncResizableState();
            }
        }

        override protected function layoutChrome(unscaledWidth:Number, unscaledHeight:Number):void {
            super.layoutChrome(unscaledWidth, unscaledHeight);

            if (movable && titleTextField) {
                // the move handle x position defaults to the title textfield's x value
                var moveX:Number = titleTextField.x;
                if (mx_internal::titleIconObject != null) {
                    // shift the title icon over, and use it's x value for the move handle
                    moveX = mx_internal::titleIconObject.x;
                    mx_internal::titleIconObject.x += MOVE_HANDLE_SIZE;
                }
                moveHandle.move(moveX, titleTextField.y);
                titleTextField.x += MOVE_HANDLE_SIZE;
            }
        }

        public function get helpUrl():String {
            return this._helpUrl;
        }

        public function set helpUrl(val:String):void {
            _helpUrl = val;
        }


        /////////////////////
        // RESIZE SUPPORT
        /////////////////////

        // draws a 14x14 resize handle like in Windows
        protected function drawResizeHandle(unscaledWidth:Number, unscaledHeight:Number):void {
            // add last
            if (resizeHandle.parent == null) {
                rawChildren.addChild(resizeHandle);
            }

            var g:Graphics = resizeHandle.graphics;
            g.clear();
            if ((unscaledWidth >= RESIZE_HANDLE_SIZE) && (unscaledHeight > RESIZE_HANDLE_SIZE)) {
                var offset:int = 6;
                drawResizeDot(g, unscaledWidth - (offset + 8), unscaledHeight - offset);
                drawResizeDot(g, unscaledWidth - (offset + 4), unscaledHeight - offset);
                drawResizeDot(g, unscaledWidth - (offset + 4), unscaledHeight - (offset + 4));
                drawResizeDot(g, unscaledWidth - offset, unscaledHeight - (offset + 4));
                drawResizeDot(g, unscaledWidth - offset, unscaledHeight - (offset + 8));
            }
        }

        protected function drawResizeDot(g:Graphics, xx:Number, yy:Number):void {
            g.lineStyle(0, 0, 0);
            g.beginFill(dotColor);
            g.drawRect(xx, yy, 2, 2);
            g.endFill();
        }

        // draws a 7x7 resize handle
        private function drawResizeHandleOld(unscaledWidth:Number, unscaledHeight:Number):void {
            graphics.clear();
            if ((unscaledWidth >= 7) && (unscaledHeight > 7)) {
                graphics.lineStyle(2);
                graphics.moveTo(unscaledWidth - 6, unscaledHeight - 1);
                graphics.curveTo(unscaledWidth - 3, unscaledHeight - 3, unscaledWidth - 1, unscaledHeight - 6);
                graphics.moveTo(unscaledWidth - 6, unscaledHeight - 4);
                graphics.curveTo(unscaledWidth - 5, unscaledHeight - 5, unscaledWidth - 4, unscaledHeight - 6);
            }
        }


        // Resize panel event handler
        protected function resizeHandler(event:MouseEvent):void {
            // Determine if the mouse pointer is over the resize handle
            // Lower left corner of panel
            var lowerLeftX:Number = x + width;
            var lowerLeftY:Number = y + height;

            // Upper left corner of 14x14 hit area
            var upperLeftX:Number = lowerLeftX - RESIZE_HANDLE_SIZE;
            var upperLeftY:Number = lowerLeftY - RESIZE_HANDLE_SIZE;

            // Mouse position in Canvas
            var panelRelX:Number = event.localX + x;
            var panelRelY:Number = event.localY + y;

            // See if the mouse cursor is in the resize handle portion of the panel.
            if (upperLeftX <= panelRelX && panelRelX <= lowerLeftX && upperLeftY <= panelRelY && panelRelY <= lowerLeftY) {
                if (event.type == MouseEvent.MOUSE_MOVE) {
                    if (CursorManager.currentCursorID == CursorManager.NO_CURSOR) {
                        CursorManager.setCursor(IconConstant.RESIZE_CURSOR);
                    }
                }
                else if (event.type == MouseEvent.MOUSE_DOWN) {
                    event.stopPropagation();
                    startResize(event.stageX, event.stageY);
                }
                else { // MOUSE_OUT
                    if (event.relatedObject != resizeHandle && event.relatedObject != this)
                        if (CursorManager.currentCursorID != CursorManager.NO_CURSOR && !_resizing) {
                            CursorManager.removeCursor(CursorManager.currentCursorID);
                        }
                }
            }
            else {
                if (CursorManager.currentCursorID != CursorManager.NO_CURSOR && !_resizing) {
                    CursorManager.removeCursor(CursorManager.currentCursorID);
                }
            }
        }

        protected function startResize(globalX:Number, globalY:Number):void {
            resizeInitX = globalX;
            resizeInitY = globalY;
            _resizing = true;
            // Add event handlers so that the SystemManager handles the mouseMove and mouseUp events.
            // Set useCapure flag to true to handle these events
            // during the capture phase so no other component tries to handle them.
            systemManager.addEventListener(MouseEvent.MOUSE_MOVE, resizeMouseMoveHandler, true);
            systemManager.addEventListener(MouseEvent.MOUSE_UP, resizeMouseUpHandler, true);
        }

        // Resizes this panel as the user moves the cursor with the mouse key down.
        protected function resizeMouseMoveHandler(event:MouseEvent):void {
            event.stopImmediatePropagation();

            var newWidth:Number = this.width + event.stageX - resizeInitX;
            var newHeight:Number = this.height + event.stageY - resizeInitY;

            // restrict the width/height
            if ((newWidth >= minWidth) && (newWidth <= maxWidth)) {
                this.width = newWidth;
            }
            if ((newHeight >= minHeight) && (newHeight <= maxHeight)) {
                this.height = newHeight;
            }

            resizeInitX = event.stageX;
            resizeInitY = event.stageY;
        }

        // Removes the event handlers from the SystemManager.
        protected function resizeMouseUpHandler(event:MouseEvent):void {
            event.stopImmediatePropagation();
            _resizing = false;
            systemManager.removeEventListener(MouseEvent.MOUSE_MOVE, resizeMouseMoveHandler, true);
            systemManager.removeEventListener(MouseEvent.MOUSE_UP, resizeMouseUpHandler, true);
        }

        /////////////////////
        // MOVE SUPPORT
        /////////////////////

        // draws a 6x14 move/drag handle
        protected function drawMoveHandle(unscaledWidth:Number, unscaledHeight:Number):void {
            var g:Graphics = moveHandle.graphics;
            g.clear();
            var xx:int = 0;
            var yy:int = 4;
            for (var i:int = 0; i < 4; i++) {
                drawResizeDot(g, xx, yy + (i * 4));
                drawResizeDot(g, xx + 4, yy + (i * 4));
            }
        }

        private function titleBarMouseDown(event:MouseEvent):void {
            // special case - ignore if the target is a button (e.g. close button)
            if (event.target is Button) {
                return;
            }

            moveInitX = event.currentTarget.mouseX;
            moveInitY = event.currentTarget.mouseY;

            parent.addEventListener(DragEvent.DRAG_ENTER, dragEnter);
            parent.addEventListener(DragEvent.DRAG_DROP, dragDrop);

            var ds:DragSource = new DragSource();
            ds.addData(this, 'EscapeWindow');
            DragManager.doDrag(this, ds, event);
        }

        private function dragEnter(event:DragEvent):void {
            if (event.target is IUIComponent) {
                DragManager.acceptDragDrop((event.target as IUIComponent));
            }
        }

        private function dragDrop(event:DragEvent):void {
            // Compensate for the mouse pointer's location in the title bar.
            var newX:int = event.currentTarget.mouseX - moveInitX;
            event.dragInitiator.x = newX;
            var newY:int = event.currentTarget.mouseY - moveInitY;
            event.dragInitiator.y = newY;

            // Put the dragged item on top of all other components.
            parent.setChildIndex((event.dragInitiator as DisplayObject), parent.numChildren - 1);

            parent.removeEventListener(DragEvent.DRAG_ENTER, dragEnter);
            parent.removeEventListener(DragEvent.DRAG_DROP, dragDrop);
        }

        //Handle CloseEvent
        protected function closeHandler(event:CloseEvent):void {
            if (this._closeable && this.parent && this.enabled) {
                this.parent.removeChild(this);
            }
            event.stopPropagation();
        }

        //Listen for keyboard event need window to be focused.
        //If click on popup window itself, it will lose focus by default.
        protected function onMouseClick(event:MouseEvent):void {
            if (event.target == event.currentTarget) {
                this.setFocus();
                event.stopPropagation();
            }
        }

    }
}


